package com.xzj.mybatis.core;

import com.xzj.mybatis.utils.JdbcTemplate;
import lombok.Builder;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ProjectName: SpringDemo
 * @Package: com.xzj.mybatis.core
 * @ClassName: DefaultSqlSessionFactory
 * @Author: xzj
 * @Description:
 * @Date: 2022/7/21 10:52
 * @Version: 1.0
 */
@Builder
public class DefaultSqlSessionFactory implements SqlSessionFactory {

    /**
     * 初始连接
     */
    private final Integer initialSize;

    /**
     * 最大连接
     */
    private final Integer maxActive;

    /**
     * 最大等待时间
     */
    private final Integer maxWait;

    /**
     * 多数据源默认值
     */
    private final String primary;

    /**
     * 多数据源信息
     */
    private final Map<String, DataSourceInfo> dataSourceInfoMap;

    /**
     * 保存多数据源的初始连接
     */
    private final Map<String, ArrayBlockingQueue<Connection>> connectionPoolMap = new HashMap<>();

    /**
     * 可创建的连接数
     */
    private final AtomicInteger canConnection = new AtomicInteger();

    /**
     * @param
     * @return
     * @Author xzj
     * @Description 初始化工厂
     * @Date 18:07 2022/7/21
     **/
    public DefaultSqlSessionFactory init() {
        canConnection.compareAndSet(0, maxActive - initialSize);
        Set<Map.Entry<String, DataSourceInfo>> entries = dataSourceInfoMap.entrySet();
        for (Map.Entry<String, DataSourceInfo> entry : entries) {
            String key = entry.getKey();
            DataSourceInfo dataSourceInfo = entry.getValue();
            Optional.ofNullable(initialSize).orElseThrow(() -> new RuntimeException("连接池初始连接参数initialSize未配置"));
            //创建连接并保存
            ArrayBlockingQueue<Connection> connections = new ArrayBlockingQueue<>(initialSize);
            for (int i = 0; i < initialSize; i++) {
                connections.add(newConnection(dataSourceInfo));
            }
            connectionPoolMap.put(key, connections);
        }
        return this;
    }

    /**
     * @param dataSourceInfo
     * @return
     * @Author xzj
     * @Description 创建数据库连接
     * @Date 18:03 2022/7/21
     **/
    private Connection newConnection(DataSourceInfo dataSourceInfo) {
        String driverClassName = dataSourceInfo.getDriverClassName();
        String url = dataSourceInfo.getUrl();
        String username = dataSourceInfo.getUsername();
        String password = dataSourceInfo.getPassword();
        try {
//            Class.forName(driverClassName);
            return DriverManager.getConnection(url, username, password);
        } catch (SQLException  e) {
           throw new RuntimeException(String.format("创建连接失败 driverClassName='%s' url='%s' username='%s' password='%s'",driverClassName,url,username,password));
        }
    }

    /**
     * @param
     * @return
     * @Author xzj
     * @Description 直接打开连接
     * @Date 11:41 2022/7/21
     **/
    @Override
    public DefaultSqlSession openSession() {
        return openSession(primary);
    }

    /**
     * @param datasource
     * @return
     * @Author xzj
     * @Description 多数据源打开连接
     * @Date 11:41 2022/7/21
     **/
    @Override
    public DefaultSqlSession openSession(String datasource) {
        try {
            boolean isInterim = false;
            //根据数据源获取 连接池
            ArrayBlockingQueue<Connection> connectionPool = connectionPoolMap.get(datasource);
            //连接池中获取连接
            Connection connection = connectionPool.poll(maxWait, TimeUnit.MILLISECONDS);
            //连接没了 判断是否可以创建新的连接
            if (connection == null) {
                while (!isInterim) {
                    int i = canConnection.get();
                    if (i <= 0) {
                        throw new RuntimeException("链接数达到最大，不能再新建了");
                    }
                    isInterim = canConnection.compareAndSet(i, i - 1);
                    if(isInterim){
                        isInterim = true;
                        connection = newConnection(dataSourceInfoMap.get(datasource));
                    }
                }
            }
            //返回连接 和是否是临时连接
            return DefaultSqlSession.builder()
                    .connectionPool(connectionPool)
                    .canConnection(canConnection)
                    .jdbcTemplate(JdbcTemplate.builder().connection(connection).build())
                    .isInterim(isInterim)
                    .build();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

}
